//爬取小说接口
'use strict';
/**
 * 如果你觉得unicloud云存储，不满足您的需求，可直接对接阿里的oss云存储，下面是配置阿里云存储
 * ali-oss流程：开通oss服务——>创建存储空间(设置权限)——>上传文件
 **/
// const OSS = require('ali-oss');
// const client = new OSS({
//   region: '****',
//   accessKeyId: '*****',
//   accessKeySecret: '****',
//   bucket: '****'//你自己创建的bucket名称
// });
//参数UNIAPP默认连接的是uniapp的云存储，ALIOSS为阿里oss，如果想连接oss只需改这里配置
let connectType='UNIAPP'
/**阿里云配置完成**/ 

let cheerio = require('cheerio'); //爬虫
let iconv = require('iconv-lite'); //处理gbk编码的网页
let Entities = require('html-entities').XmlEntities;
let entities = new Entities();
const fs = require('fs')
const path = require('path')
const db = uniCloud.database()
const collection = db.collection('repiles-book')
const dbCom = db.command
//每个小说爬取多少章默认是所有章节，爬完后再爬取下个小说，可设置每个小说爬取12个章节
let config_chapters=''
// const API = 'https://www.xbiquge.la'
const API = 'http://www.6lk.net'
//爬取时只需要更改参数bookType就可对应爬取小说
let bookType = 1 //1为玄幻，2武侠，3都市，4穿越，5仙侠，6恐怖，7言情
let constUrl,bookTypeTxt
if(bookType==1){
	constUrl = API + '/sort/10/' //最新玄幻小说
	bookTypeTxt = "玄幻小说"
}else if(bookType==2){
	constUrl = API + '/sort/6/' //最新武侠小说
	bookTypeTxt = "武侠小说"
}else if(bookType==3){
	constUrl = API + '/sort/3/' //最新都市小说
	bookTypeTxt = "都市小说"
}else if(bookType==4){
	constUrl = API + '/sort/4/' //最新穿越小说
	bookTypeTxt = "穿越小说"
}else if(bookType==5){
	constUrl = API + '/sort/5/' //最新仙侠小说
	bookTypeTxt = "仙侠小说"
}else if(bookType==6){
	constUrl = API + '/sort/7/' //最新恐怖小说
	bookTypeTxt = "恐怖小说"
}else if(bookType==7){
	constUrl = API + '/sort/1/' //最新言情小说
	bookTypeTxt = "言情小说"
}
const host = constUrl + '1.html'

exports.main = async (event, context) => {
	//发送请求获取页面内容
	var body = await requestFn(host)
	body = iconv.decode(body, 'utf-8'); //转换gbk编码的页面内容
	let $ = cheerio.load(body, {
		decodeEntities: false
	});
	//获取当前url网址小说总页数
	let a_length = $('.pages a').length
	let a_href = $('.pages a').eq(a_length-1).attr('href')
	var index = a_href.lastIndexOf('/');
	let pageSize = parseInt(a_href.substring(index + 1, a_href.length-5));
	console.log('总页数' + pageSize);
	let res = {}
	//云函数爬取小说
	if (event.active == 'crawlingBook') {
		res = await getforEachBoook(100)
	}else if(event.active == 'crawlingOneBook'){//云端爬取单个小说
		await getAppointBook(event.params.cheerioUrl)
	}else if(event.active == 'config_page_craw'){//本地页面配置爬取参数，进行爬取
		let config_pageAll = event.params.pageAll //爬取的小说总页数
		let config_pageNow = event.params.pageNow //爬取小说第几页
		config_chapters = event.params.chapters //每个小说爬取多少章节，默认12
		//1为玄幻，2武侠，3都市，4穿越，5仙侠，6恐怖，7言情
		let bookTypeTxtList = [{id:1,name:'玄幻'},{id:2,name:'武侠'},{id:3,name:'都市'},{id:4,name:'穿越'},{id:5,name:'仙侠'},{id:6,name:'恐怖'},{id:7,name:'言情'}]
		bookType = event.params.bookType
		bookTypeTxt = (bookTypeTxtList.find(item=>item.id==event.params.bookType)).name + '小说'
		console.log(`自定义爬取--总页数${config_pageAll}-第${config_pageNow}页-爬取${config_chapters}章节--【${bookTypeTxt}】`);
		res = await getforEachBoook(config_pageAll,config_pageNow)
	}else{
		// 本地运行云函数时 循环获取网站每一页的小说内容
		await getforEachBoook(100)
	}
	
	//返回数据给客户端
	return res
};
//爬取指定小说
//传入书的原始页面
async function getAppointBook(cheerioUrl){
	var body = await requestFn(cheerioUrl)
	body = iconv.decode(body, 'utf8'); //转换gbk编码的页面内容
	let $ = cheerio.load(body);
	//1为玄幻，2武侠，3都市，4穿越，5仙侠，6恐怖，7言情
	let bookTypeTxtList = [{id:1,name:'玄幻'},{id:2,name:'武侠'},{id:3,name:'都市'},{id:4,name:'穿越'},{id:5,name:'仙侠'},{id:6,name:'恐怖'},{id:7,name:'言情'}]
	let li = $('#bookinfo .bookright .count ul li')
	let bookTypeTxt = li.eq(0).find('span').html()
	let bookType = (bookTypeTxtList.find(item=>item.name==bookTypeTxt)).id
	let bookName = $('#bookinfo .booktitle h1').html()
	let bookAuthor = $('#author').html().replace('作者：','')
	let lastChapter = $('#readerlist ul li').last().find('a').attr('title')
	let bookInfo = {
		cheerioUrl,//爬取小说的原始地址
		bookType,//1玄幻小说2都市
		bookTypeTxt:bookTypeTxt+'小说', //类型描述
		bookName,//书的标题
		bookAuthor,//书的作者
		lastChapter,//最新章节
	}
	let res = await getForEachBookInfo($,bookInfo)
}

//循环获取每一页的内容
async function getforEachBoook(pageSize,pageNow=0) {
	for (let a = pageNow; a < pageSize; a++) {
		let host = `${constUrl}${a}.html`
		console.log(host);
		//发送请求获取页面内容
		var body = await requestFn(host)
		body = iconv.decode(body, 'utf8'); //转换gbk编码的页面内容
		let $ = cheerio.load(body);
		//获取小说列表
		let list = $('#sitebox dl')
		//循环请求获取小说详情介绍
		for (var i = 0; i < list.length; i++) {
			//书的详情页
			let href = API+ list.eq(i).find('dt a').attr("href")
			//获取书的标题
			let bookName = list.eq(i).find('dd').eq(0).find('a').html()
			//获取书的作者
			let bookAuthor = list.eq(i).find('.book_other').eq(0).find('span').eq(0).html()
			//最新章节
			let lastChapter = list.eq(i).find('.book_other').eq(1).find('a').html()
			let bookInfo = {
				cheerioUrl:href,//爬取小说的原始地址
				bookType,//1玄幻小说2都市
				bookTypeTxt, //类型描述
				bookName,//书的标题
				bookAuthor,//书的作者
				lastChapter,//最新章节
			}
			console.log(`爬取【${bookTypeTxt}】-第${a}页-第${i}本小说`)
			//发送请求获取页面内容
			var body = await requestFn(href)
			body = iconv.decode(body, 'utf8'); //转换gbk编码的页面内容
			let $$ = cheerio.load(body);
			//循环获取当前页的小说基本信息【书的封面，书的标题，书的作者，书的id，书的简介】
			let arrList = await getForEachBookInfo($$,bookInfo)
		}
	}
}
//循环获取当前页的小说基本信息【书的封面，书的标题，书的作者，书的id，书的简介】
async function getForEachBookInfo($,bookInfo) {
	//书的简介
	let bookSynopsis = $('#bookintro p').html()
	//最后更新
	let lastUpdate = $('#bookinfo .bookright .new .uptime span').html()
	let li = $('#bookinfo .bookright .count ul li')
	//周点击
	let weeklyClick = parseInt(li.eq(1).find('span').html())
	//月点击
	let monthClick = parseInt(li.eq(2).find('span').html())
	//总点击
	let allClick = parseInt(li.eq(3).find('span').html())
	//小说状态
	let bookNetType = li.eq(4).find('span').html()
	//总推荐
	let allRecommend = parseInt(li.eq(5).find('span').html())
	//总字数
	let allTotal = parseInt(li.eq(6).find('span').html())
	//书的封面
	let bookImageSrc =API+ $('#bookinfo #bookimg img').attr("src")
	let obj = {
		...bookInfo,
		bookImageSrc,//书的封面
		bookSynopsis, //书的简介
		bookNetType,//书的状态
		lastUpdate,//最后更新时间
		weeklyClick,//周点击
		monthClick,//月点击
		allClick,//总点击
		allRecommend,//总推荐
		allTotal,//总字数
	}
	// 查询数据库是否存在此书【没有就添加】根据书的作者和书名确定书是否存在
	let commentDB = await db.collection('bookList').where({
		bookName: obj.bookName,
		bookAuthor: obj.bookAuthor
	}).get()
	//此书数据库不存在存入
	if (commentDB.data.length == 0) {
		//把书基本信息添加到数据库中
		let Result = await db.collection('bookList').add(obj) //单条添加
		//拿到id
		//在另外的集合中存储书的目录章节,传入当前存入书的id
		await setChapter($, Result.id,obj)
	} else {
		//如果这本书已经存储，对比下更新时间是否一致，如果更新日期不一致说明小说更新了
		if(commentDB.data.lastUpdate!==obj.lastUpdate){
			await setChapter($, commentDB.data[0]._id,obj)
		}else{
			console.log('当前小说目录暂无更新');
		}
	}
}
//存储书的目录
async function setChapter($, _id,obj) {
	//在获取数据库中当前小说的所有目录
	let res = await db.collection('bookMenu').where({_id}).get();
	let meunList=res.data[0]&&res.data[0].chapterList||[]
	console.log('小说【'+obj.bookName+'】共'+$('#readerlist a').length+'章节');
	await copeData(meunList.length,$, _id,obj,[])
}
async function copeData(i,$,_id,obj,chapterList=[]){
	let menu = $('#readerlist a')
	//如果传入每个小说爬取几章则爬取几章，后再爬下个小说，如果没传则爬取小说所有章节
	let num = parseInt(config_chapters||menu.length)
	console.log('已经爬取了'+i+'章节','共'+num+'章节');
	//menu.length
	if(i<num){
		let href=menu.eq(i).attr('href')//网站章节url地址（目前没有上传云存储）
		let name=menu.eq(i).html()//章节名称
		console.log('正在写入【'+obj.bookName+'】-'+name+'-'+obj.bookTypeTxt);
		try{
			//发送请求获取页面内容
			let body = await requestFn(API+href)
			body = iconv.decode(body, 'utf8'); //转换gbk编码的页面内容
			let $$ = cheerio.load(body);
			let content= $$("#content").html()
			let urlname=obj.bookAuthor+'/'+obj.bookName+'/'+name+'.json'//书的作者+书名+章节来作为上传云存储的名称
			//向云存储中写入章节内容
			if(connectType=='UNIAPP'){
				let res= await wirteOSS(content,urlname)
				console.log('上传结果',res.fileID);
				let object = {
					name, //目录名称
					fileID:res.fileID
				}
				chapterList.push(object)
			}else if(connectType=='ALIOSS'){
				let res = await AliOSS(content,urlname)
				let object = {
					name, //目录名称
					fileID:res.url
				}
				chapterList.push(object)
			}                                                       
		}catch (e){
			console.log('出错了'+e);
			//操作数据库
			await collectionDB(_id,obj,chapterList)
			chapterList=[]
			return
		}
		//每获取到3个章节就向数据库写入，防止云函数超时，数据保存失败
		if(chapterList.length==6){
			console.log('存储下，防止超时');
			//操作数据库
			await collectionDB(_id,obj,chapterList)
			chapterList=[]
		}
		i++
		//递归请求
		await copeData(i,$,_id,obj,chapterList)
	}else{
		//操作数据库
		await collectionDB(_id,obj,chapterList)
		chapterList=[]
	}
	
}
//操作数据库添加章节
async function collectionDB(_id,obj,chapterList){
	let rem = await db.collection('bookMenu').doc(_id).get();
	let meunList=rem.data[0]&&rem.data[0].chapterList||[];
	if(chapterList.length==0) return
	//当数据库没有此书时直接添加目录
	if(meunList.length==0){
		let data={
			_id,
			bookName:obj.bookName,
			chapterList
		}
		let Result = await db.collection('bookMenu').add(data) //单个添加
	}else {//发现更新目录
		let res = await db.collection('bookMenu').doc(_id).update({
		  chapterList:meunList.concat(chapterList)
		});
	}
}
//上传到云存储
async function wirteOSS(content,urlname) {
	//阿里云直接把字符串转成buff上传就可以了
	let buffer = Buffer.from(JSON.stringify(content))
	let result = await uniCloud.uploadFile({
		cloudPath:urlname,//阿里云直接写名字，可以不用全路径
		fileContent: buffer//阿里云内容不支持流所有不能使用fs.createReadStream()来读取流
	});
	return result
}
//上传阿里云oss
async function AliOSS(content,urlname) {
	//阿里云直接把字符串转成buff上传就可以了
	let buffer = Buffer.from(JSON.stringify(content))
	let result = await client.put(urlname,buffer)
	return result
}


// //前端动态获取当前小说最新章节
// async function getLatestChapters(event) {
// 	let result = await db.collection('bookList').where({
// 		bookId: parseInt(event.params.bookId)
// 	}).get()
// 	let result2 = await db.collection('bookList2').where({
// 		bookId: parseInt(event.params.bookId)
// 	}).get()
// 	if (result.data && result.data.length > 0) {
// 		//循环获取小说目录
// 		let arrBookCatalog = await getForEachBookCatalog(result.data)
// 	}
// 	if (result2.data && result2.data.length > 0) {
// 		//循环获取小说目录
// 		let arrBookCatalog = await getForEachBookCatalog(result2.data)
// 	}
// }

//发送请求获取页面内容
async function requestFn(url) {
	const res = await uniCloud.httpclient.request(url, {
		method: 'GET',
		timeout: '60000',
		followRedirect: true //如果返回301或3XX后开启自动重定向
	})
	return res.data
}
//防止请求过快，进行停留多少时间在运行下一个代码
function sleep(endTime) {
	var start=new Date().getTime();//定义起始时间的毫秒数
	while(true){
		var time=new Date().getTime();//每次执行循环取得一次当前时间的毫秒数
		if(time-start>endTime){//如果当前时间的毫秒数减去起始时间的毫秒数大于给定的毫秒数，即结束循环
			break;
		}
	}
}
